{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 1\n",
    "\n",
    "Напишите функцию, которая возвращает название валюты (поле ‘Name’) с максимальным значением курса с помощью сервиса https://www.cbr-xml-daily.ru/daily_json.js"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "import json\n",
    "import traceback\n",
    "from decimal import Decimal\n",
    "from abc import ABC, abstractmethod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "СДР (специальные права заимствования)\n"
     ]
    }
   ],
   "source": [
    "def get_web_data(url:str)->tuple:\n",
    "    \"\"\"Функция позволяет получить веб-данные по указанному url.\n",
    "    \n",
    "    Аргументы функции:\n",
    "    url -- строка в формате http\n",
    "    \n",
    "    Возвращаемое значение: (код завершения функции, декодированный веб-контент или None)\n",
    "    \"\"\"\n",
    "    \n",
    "    if not isinstance(url, str):\n",
    "        raise ValueError('Переменная url должна иметь тип str')\n",
    "    \n",
    "    # Получаем данные из API, таймаут 10 секунд\n",
    "    try:\n",
    "        response = requests.get(url, timeout=10)\n",
    "    except:\n",
    "        extend_error = traceback.format_exc()\n",
    "        print(f'Не удалось получить данные из сервиса {url}\\n{extend_error}')\n",
    "        return 1, None\n",
    "    \n",
    "    # Если веб-сервер возвращает код отличный от 200, значит произошла ошибка, уведомляем пользователя, возвращаем None\n",
    "    if response.status_code != 200:\n",
    "        print(f'Не удалось получить данные из сервиса {url}, сервер вернул ошибку {response.status_code} {response.reason}')\n",
    "        return 2, None\n",
    " \n",
    "    return 0, response.content.decode(response.encoding)\n",
    "    \n",
    "    \n",
    "def get_exhange_rates()->dict:\n",
    "    \"\"\"Функция получает курсы валют из сервиса https://www.cbr-xml-daily.ru/daily_json.js\n",
    "    \n",
    "    Возвращаемое значение: словарь курсов валют или пустой словарь\n",
    "    \n",
    "    Формат возвращаемого словаря:\n",
    "    {\"КОД ВАЛЮТЫ\": {\n",
    "                    \"ID\": ЗНАЧЕНИЕ ИДЕНТИФИКАТОРА ВАЛЮТЫ тип str,\n",
    "                    \"NumCode\": ЗНАЧЕНИЕ КОДА ВАЛЮТЫ тип str,\n",
    "                    \"CharCode\": СИМВОЛЬНОЕ ОБОЗНАЧЕНИЕ ВАЛЮТЫ тип str,\n",
    "                    \"Nominal\":  НОМИНАЛ ВАЛЮТЫ тип int,\n",
    "                    \"Name\": НАЗВАНИЕ ВАЛЮТЫ  тип str,\n",
    "                    \"Value\": ТЕКУЩЕЕ ЗНАЧЕНИЕ КУРСА ВАЛЮТЫ  тип float,\n",
    "                    \"Previous\": ПРЕДЫДУЩЕЕ ЗНАЧЕНИЕ КУРСА ВАЛЮТЫ  тип float\n",
    "                    }\n",
    "    }\n",
    "    \"\"\"\n",
    "    \n",
    "    # Ссылка на сервис для получения курсов валют\n",
    "    url = 'https://www.cbr-xml-daily.ru/daily_json.js'\n",
    "    \n",
    "    # Поулчаем даныне из сервиса, в случае ошибки получения данных уведомляем пользователя и возвращаем пустой словарь\n",
    "    return_code, web_content = get_web_data(url)\n",
    "    if return_code != 0:\n",
    "        print('Не удалось сформировать словарь курсов валют из-за ошибки получения данных из API')\n",
    "        return {}\n",
    " \n",
    "    # Формируем итоговый словарь функции, в случае ошибки во время операции уведомляем пользователя \n",
    "    # и возвращаем пустой словарь\n",
    "    try:\n",
    "        exhange_rates = json.loads(web_content)['Valute']\n",
    "    except:\n",
    "        extend_error = traceback.format_exc()\n",
    "        print(f'Не удалось сформировать словарь валют после получения данных из API\\n{extend_error}')\n",
    "        return {}\n",
    "        \n",
    "    return exhange_rates\n",
    "\n",
    "\n",
    "def name_currency_with_maximum_value(exhange_rates:dict)->str:\n",
    "    \"\"\"\n",
    "    Функция для поиска валюты с максималным значением курса.\n",
    "    \n",
    "    Аргументы функции:\n",
    "    exhange_rates -- словарь курсов валют. \n",
    "    Словарь Должен иметь формат:\n",
    "        {\"КОД ВАЛЮТЫ\": {\n",
    "                    \"Name\": НАЗВАНИЕ ВАЛЮТЫ  тип str,\n",
    "                    \"Value\": ТЕКУЩЕЕ ЗНАЧЕНИЕ КУРСА ВАЛЮТЫ  тип float\n",
    "                    }\n",
    "          ...\n",
    "        }\n",
    "        \n",
    "    Возвращаемое значение: полное наименование валюты или None\n",
    "    \"\"\"\n",
    "    \n",
    "    if not isinstance(exhange_rates, dict):\n",
    "        raise ValueError('Переменная exhange_rates должна иметь тип dict')\n",
    "    \n",
    "    # Сортируем словарь по значению текущего курса от большего к меньшему\n",
    "    try:\n",
    "        sorted_exhange_rates = sorted(exhange_rates.values(), key=lambda x: x['Value'], reverse=True)\n",
    "    except:\n",
    "        extend_error = traceback.format_exc()\n",
    "        print(f'Произошла ошибка при сортировке словаря exhange_rates\\n{extend_error}')\n",
    "        return None\n",
    "    \n",
    "    return sorted_exhange_rates[0]['Name']\n",
    "    \n",
    "def main():    \n",
    "    exhange_rates = get_exhange_rates()\n",
    "    if exhange_rates:\n",
    "        print(name_currency_with_maximum_value(exhange_rates))\n",
    "    else:\n",
    "        print('Название валюты с максимальным курсом не найдено')\n",
    "        \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 2\n",
    "\n",
    "Добавьте в класс Rate параметр diff (со значениями True или False), который в случае значения True в методах курсов валют (eur, usd итд) будет возвращать не курс валюты, а изменение по сравнению в прошлым значением. Считайте, self.diff будет принимать значение True только при возврате значения курса. При отображении всей информации о валюте он не используется."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Rate:\n",
    "    def __init__(self, format_:str='value', diff:bool=False):\n",
    "        \n",
    "        self.__check_format_param(format_)\n",
    "        self.__check_diff_param(diff)\n",
    "\n",
    "        self._format = format_.lower()\n",
    "        self._diff = diff\n",
    "        \n",
    "    @staticmethod\n",
    "    def __check_format_param(format_):\n",
    "        \"\"\"\n",
    "        Функция проверки параметра format_ при пользовательском изменении  \n",
    "        \"\"\"\n",
    "        if format_.lower() not in ('value', 'full'):\n",
    "            raise ValueError('Параметр format_ должен иметь значение \"value\" или \"full\"')\n",
    "            \n",
    "    @staticmethod\n",
    "    def __check_diff_param(diff):\n",
    "        \"\"\"\n",
    "        Функция проверки параметра diff при пользовательском изменении \n",
    "        \"\"\"\n",
    "        if not isinstance(diff, bool):\n",
    "            raise ValueError('Параметр diff должен иметь тип bool')\n",
    "    \n",
    "    @property\n",
    "    def format(self):\n",
    "        return self._format\n",
    "    @format.setter\n",
    "    def format(self, format):\n",
    "        self.__check_format_param(format)\n",
    "        self._format = format\n",
    "        \n",
    "    @property\n",
    "    def diff(self):\n",
    "        return self._diff\n",
    "    @diff.setter\n",
    "    def diff(self, diff):\n",
    "        self.__check_diff_param(diff)\n",
    "        self._diff = diff\n",
    "    \n",
    "    def exchange_rates(self):\n",
    "        \"\"\"\n",
    "        Возвращает ответ сервиса с информацией о валютах в виде:\n",
    "        \n",
    "        {\n",
    "            'AMD': {\n",
    "                'CharCode': 'AMD',\n",
    "                'ID': 'R01060',\n",
    "                'Name': 'Армянских драмов',\n",
    "                'Nominal': 100,\n",
    "                'NumCode': '051',\n",
    "                'Previous': 14.103,\n",
    "                'Value': 14.0879\n",
    "                },\n",
    "            ...\n",
    "        }\n",
    "        \"\"\"\n",
    "        \n",
    "        # Получаем данные из API, таймаут 10 секунд. В случае ошибки возвращаем None\n",
    "        url = 'https://www.cbr-xml-daily.ru/daily_json.js'\n",
    "        try:\n",
    "            self._r = requests.get(url, timeout=10)\n",
    "        except:\n",
    "            return None\n",
    "\n",
    "        # Если веб-сервер возвращает код отличный от 200, значит произошла ошибка и возвращаем None\n",
    "        if self._r.status_code != 200:\n",
    "            return None\n",
    "        \n",
    "        return self._r.json()['Valute']\n",
    "    \n",
    "    def make_format(self, currency):\n",
    "        \"\"\"\n",
    "        Возвращает информацию о валюте currency в двух вариантах:\n",
    "        - полная информация о валюте при self._format = 'full':\n",
    "        Rate('full').make_format('EUR')\n",
    "        {\n",
    "            'CharCode': 'EUR',\n",
    "            'ID': 'R01239',\n",
    "            'Name': 'Евро',\n",
    "            'Nominal': 1,\n",
    "            'NumCode': '978',\n",
    "            'Previous': 79.6765,\n",
    "            'Value': 79.4966\n",
    "        }\n",
    "        \n",
    "        Rate('value').make_format('EUR')\n",
    "        79.4966\n",
    "        \"\"\"\n",
    "        response = self.exchange_rates()\n",
    "        if response is None:\n",
    "            return 'Error'\n",
    "        \n",
    "        if currency in response:\n",
    "            if self._format == 'full':\n",
    "                return response[currency]\n",
    "            \n",
    "            if self._format == 'value':\n",
    "                if self._diff:\n",
    "                    return Decimal(str(response[currency]['Value'])) - Decimal(str(response[currency]['Previous']))     \n",
    "                else:\n",
    "                    return response[currency]['Value']\n",
    "        \n",
    "        return 'Error'\n",
    "    \n",
    "    def eur(self):\n",
    "        \"\"\"Возвращает курс евро на сегодня в формате self.format\"\"\"\n",
    "        return self.make_format('EUR')\n",
    "    \n",
    "    def usd(self):\n",
    "        \"\"\"Возвращает курс доллара на сегодня в формате self.format\"\"\"\n",
    "        return self.make_format('USD')\n",
    "    \n",
    "    def brl(self):\n",
    "        \"\"\"Возвращает курс бразильского реала на сегодня в формате self.format\"\"\"\n",
    "        return self.make_format('BRL')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Разница курса EUR: -0.0969\n",
      "Текущий курс EUR: 89.0384\n",
      "Подробная инфомрация о курсе EUR: {'ID': 'R01239', 'NumCode': '978', 'CharCode': 'EUR', 'Nominal': 1, 'Name': 'Евро', 'Value': 89.0384, 'Previous': 89.1353}\n"
     ]
    }
   ],
   "source": [
    "rate_obj = Rate(format_='value', diff=True)\n",
    "print('Разница курса EUR:', rate_obj.eur())\n",
    "rate_obj.diff = False\n",
    "print('Текущий курс EUR:', rate_obj.eur())\n",
    "rate_obj.format='full'\n",
    "print('Подробная инфомрация о курсе EUR:', rate_obj.eur())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 3\n",
    "\n",
    "Напишите класс Designer, который учитывает количество международных премий. Подсказки в коде занятия в разделе “Домашнее задание задача 3”."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Павел 1\n",
      "Павел 1\n",
      "Павел 1\n",
      "Павел 1\n",
      "Павел 1\n",
      "Павел 1\n",
      "Павел 2\n",
      "Павел 3\n",
      "Павел 3\n",
      "Павел 3\n"
     ]
    }
   ],
   "source": [
    "class EmployeeAbstract(ABC):\n",
    "    \n",
    "    @abstractmethod\n",
    "    def grade_up(self):\n",
    "        \"\"\"Повышает уровень сотрудника\"\"\"\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def publish_grade(self):\n",
    "        \"\"\"Публикация результатов аккредитации сотрудников\"\"\"\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def check_if_it_is_time_for_upgrade(self):\n",
    "        \"\"\"Проверяется требуется ли повышение уровня\"\"\"\n",
    "        pass\n",
    "    \n",
    "\n",
    "class Employee(EmployeeAbstract):\n",
    "    def __init__(self, name:str, seniority:'int >= 0'):\n",
    "        self.name = name\n",
    "        if not isinstance(seniority, int) or seniority < 0:\n",
    "            raise ValueError('Параметр seniority должен быть типом int и быть больше или равным 0')\n",
    "        self.seniority = seniority\n",
    "        \n",
    "        self.grade = 1\n",
    "        \n",
    "    def __call__(self):\n",
    "        return self.check_if_it_is_time_for_upgrade()\n",
    "    \n",
    "    def grade_up(self):\n",
    "        \"\"\"Повышает уровень сотрудника\"\"\"\n",
    "        self.grade += 1\n",
    "    \n",
    "    def publish_grade(self):\n",
    "        \"\"\"Публикация результатов аккредитации сотрудников\"\"\"\n",
    "        print(self.name, self.grade)\n",
    "    \n",
    "    def check_if_it_is_time_for_upgrade(self):\n",
    "        pass\n",
    "\n",
    "\n",
    "class Designer(Employee):\n",
    "       \n",
    "    def __call__(self, prize_count=0):\n",
    "        return self.check_if_it_is_time_for_upgrade(prize_count=prize_count)\n",
    "    \n",
    "    def check_if_it_is_time_for_upgrade(self, prize_count:'int >= 0'=0):\n",
    "        # Для каждой аккредитации увеличиваем счетчик на 1\n",
    "        # пока считаем, что все дизайнеры проходят аккредитацию\n",
    "        # Если дизайнер получил международную премию, то добавляем дополнительные 2 балла за каждую премию.\n",
    "        # Учитываем только новые премии, которые появились с момента прошлой акредитации или были не учтены ранее.\n",
    "        # Количество неучтеных международных премий на момент акредитации передаются в параметре prize_count типа int\n",
    "        \n",
    "        if not isinstance(prize_count, int) or prize_count < 0:\n",
    "            raise ValueError('Параметр prize_count должен быть типом int и быть больше или равным 0')\n",
    "        \n",
    "        bonus_points = 1 + prize_count*2 if prize_count else 1\n",
    "            \n",
    "        while bonus_points > 0:\n",
    "            self.seniority += 1\n",
    "            if self.seniority % 7 == 0:\n",
    "                self.grade_up()\n",
    "            bonus_points -= 1\n",
    "        \n",
    "        # публикация результатов\n",
    "        return self.publish_grade()\n",
    "    \n",
    "    \n",
    "designer_1 = Designer('Павел', 0)\n",
    "designer_1()\n",
    "designer_1()\n",
    "designer_1()\n",
    "designer_1()\n",
    "designer_1()\n",
    "designer_1()\n",
    "designer_1()\n",
    "designer_1(prize_count=3)\n",
    "designer_1(prize_count=1)\n",
    "designer_1()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
